﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSE.Deployment.Engine.Interfaces;
using PSE.Deployment.Engine.CustomEventArgs;
using PSE.Deployment.Engine.Enums;

namespace PSE.Deployment.Engine
{
    public abstract class BaseStep : IStep
    {
        #region Propriedades
        public string Id
        {
            get;
            set;
        }

        public bool Ignorable
        {
            get;
            set;
        }

        public string Description
        {
            get;
            set;
        }

        public StepExecutionStatus ExecutionStatus
        {
            get;
            set;
        }
        public IStepGroup Parent
        {
            get;
            set;
        }

        public StepErrorBehavior ErrorBehavior
        {
            get;
            set;
        }
        public StepNavigationBehavior NavigationBehavior
        {
            get;
            set;
        }

        public Exception ExecutionException
        {
            get;
            set;
        }

        public Exception UndoException
        {
            get;
            set;
        }

        public int CurrentProgress
        {
            get;
            private set;
        }

        public int MaximumProgress
        {
            get;
            private set;
        }

        public string ProgressDescription
        {
            get;
            private set;
        }

        /// <summary>
        /// Indica que se o Engine irá ou não realizar o salvamento dos parâmetros 
        /// </summary>
        //public bool SaveParametersOnFinished { get; set; }
        /// <summary>
        /// Indica que em caso de erros no passo, o mesmo será re-executado um numero configurado de vezes
        /// </summary>
        public bool AutomaticRetryOnError { get; set; }
        /// <summary>
        /// Número máximo de tentativas de re-execução de um passo em caso de erros
        /// </summary>
        public int MaxAttempts { get; set; }
        /// <summary>
        /// Número de tempo de espera para a re-execução de um passo em caso de erros
        /// </summary>
        public int RetryMillisecondsTimeout { get; set; }

        #endregion

        #region Construtores

        public BaseStep(IStepGroup parent)
        {
            this.AutomaticRetryOnError = false;
            this.MaxAttempts = 3;
            //this.SaveParametersOnFinished = false;
            this.RetryMillisecondsTimeout = 0;

            this.Parent = parent;
            this.Ignorable = true;

            //this.ResetProgress(100);
        }

        #endregion

        #region Método Abstratos e Virtuais
        public abstract void Execute();
        public abstract void Undo();
        #endregion

        #region Eventos
        public event EventHandler<ExecutionStartedEventArgs> ExecutionStarted;
        public event EventHandler<ExecutionFinishedEventArgs> ExecutionFinished;
        public event EventHandler<ExecutionErrorEventArgs> ExecutionError;
        public event EventHandler<ExecutionIgnoredEventArgs> ExecutionIgnored;

        public event EventHandler<ExecutionProgressEventArgs> ExecutionProgress;

        public event EventHandler<UndoStartedEventArgs> UndoStarted;
        public event EventHandler<UndoFinishedEventArgs> UndoFinished;
        public event EventHandler<UndoErrorEventArgs> UndoError;
        #endregion

        #region Métodos Concretos
        public void OnExecutionStarted(ExecutionStartedEventArgs e)
        {
            this.OnExecutionStarted(this, e);
        }

        public void OnExecutionStarted(object sender, ExecutionStartedEventArgs e)
        {
            if (this.ExecutionStarted != null)
                this.ExecutionStarted(sender, e);
        }

        public void OnExecutionFinished(ExecutionFinishedEventArgs e)
        {
            this.OnExecutionFinished(this, e);
        }

        public void OnExecutionFinished(object sender, ExecutionFinishedEventArgs e)
        {
            if (this.ExecutionFinished != null)
                this.ExecutionFinished(sender, e);
        }

        public void OnExecutionError(ExecutionErrorEventArgs e)
        {
            this.OnExecutionError(this, e);
        }

        public void OnExecutionError(object sender, ExecutionErrorEventArgs e)
        {
            if (this.ExecutionError != null)
                this.ExecutionError(sender, e);
        }

        public void OnExecutionIgnored(ExecutionIgnoredEventArgs e)
        {
            this.OnExecutionIgnored(this, e);
        }

        public void OnExecutionIgnored(object sender, ExecutionIgnoredEventArgs e)
        {
            if (this.ExecutionIgnored != null)
                this.ExecutionIgnored(sender, e);
        }

        protected void IncrementProgress()
        {
            if (this.CurrentProgress < this.MaximumProgress)
            {
                this.CurrentProgress++;
            }
            else
            {
                this.CurrentProgress = this.MaximumProgress;
            }

            this.OnExecutionProgress(new ExecutionProgressEventArgs(this, this.CurrentProgress, this.MaximumProgress, this.ProgressDescription));
        }

        protected void IncrementProgress(string progressDescription)
        {
            this.ProgressDescription = progressDescription;
            
            this.IncrementProgress();
            
            Parent.WriteStepLog(this, this.ProgressDescription);
        }

        protected void DecrementProgress()
        {
            if (this.CurrentProgress > 0)
            {
                this.CurrentProgress--;
            }
            else
            {
                this.CurrentProgress = 0;
            }

            this.OnExecutionProgress(new ExecutionProgressEventArgs(this, this.CurrentProgress, this.MaximumProgress, this.ProgressDescription));
        }

        protected void DecrementProgress(string progressDescription)
        {
            this.ProgressDescription = progressDescription;
            this.DecrementProgress();
        }

        protected void ResetProgress(int maximumProgress)
        {
            this.CurrentProgress = 0;

            //if (maximumProgress == 0)
            //    this.MaximumProgress = maximumProgress;
            
            this.MaximumProgress = maximumProgress;
            this.ProgressDescription = null;
            this.OnExecutionProgress(new ExecutionProgressEventArgs(this, this.CurrentProgress, this.MaximumProgress, this.ProgressDescription));
        }

        protected virtual void OnExecutionProgress(ExecutionProgressEventArgs e)
        {
            this.OnExecutionProgress(this, e);
        }

        protected virtual void OnExecutionProgress(object sender, ExecutionProgressEventArgs e)
        {
            if (ExecutionProgress != null)
                ExecutionProgress(sender, e);
        }

        public void OnUndoStarted(UndoStartedEventArgs e)
        {
            this.OnUndoStarted(this, e);
        }

        public void OnUndoStarted(object sender, UndoStartedEventArgs e)
        {
            if (UndoStarted != null)
                UndoStarted(sender, e);
        }

        public void OnUndoFinished(UndoFinishedEventArgs e)
        {
            this.OnUndoFinished(this, e);
        }

        public void OnUndoFinished(object sender, UndoFinishedEventArgs e)
        {
            if (UndoFinished != null)
                UndoFinished(sender, e);
        }

        public void OnUndoError(UndoErrorEventArgs e)
        {
            this.OnUndoError(this, e);
        }

        public void OnUndoError(object sender, UndoErrorEventArgs e)
        {
            if (this.UndoError != null)
                this.UndoError(sender, e);
        }

        protected bool IsStopped()
        {
            ExecutionControl.Continue.WaitOne();
            return ExecutionControl.IsStopped.WaitOne(0);
        }

        #endregion
    }
}
